﻿namespace System.NETool;

/// <summary>
/// 日期与长整形和浮点型互转
/// Value：长整形，为1970年1月1日0时起的毫秒数
/// DoubleValue：双精度浮点数，1970年1月1日0时起的天数
/// </summary>
public struct LongDateTime : IComparable, IEquatable<LongDateTime>, IEquatable<long>, IEquatable<DateTime>
{
    public long Value { get; set; }

    // ReSharper disable InconsistentNaming
    public const long Jan1st1970Ticks = 621355968000000000; //Jan1st1970.Ticks;
    public const long Dec31th9999Ticks = 3155378975999990000; //DateTime.MaxValue.Ticks;
    // ReSharper restore InconsistentNaming
    public const string DefaultFormatString = DateTimeEx.DateTimeFormatEx;
    public const double MillisecondsPerDay = 86400000.0;

    /// <summary>
    /// 返回当前时间的毫秒数, 这个毫秒其实就是自1970年1月1日0时起的毫秒数
    /// </summary>
    public static long CurrentDateTimeToTicks()
    {
        return (DateTime.UtcNow.Ticks - Jan1st1970Ticks) / 10000;
    }

    public double DoubleValue
    {
        get => Value * 1.0 / MillisecondsPerDay;
        set => Value = (long)(value * MillisecondsPerDay);
    }

    /// <summary>
    /// 返回指定时间的毫秒数, 这个毫秒其实就是自1970年1月1日0时起的毫秒数
    /// </summary>
    public static long DateTimeToTicks(DateTime datetime)
    {
        return (datetime.ToUniversalTime().Ticks - Jan1st1970Ticks) / 10000;
    }

    /// <summary>
    /// 从一个代表自1970年1月1日0时起的毫秒数，转换为DateTime (北京时间)
    /// </summary>
    public static DateTime TicksToDateTime(long ticks)
    {
#pragma warning disable CS0618 // 类型或成员已过时
        return TimeZone.CurrentTimeZone.ToLocalTime(TicksToUTCDateTime(ticks));
#pragma warning restore CS0618 // 类型或成员已过时
    }

    /// <summary>
    /// 从一个代表自1970年1月1日0时起的毫秒数，转换为DateTime (UTC时间)
    /// </summary>
    public static DateTime TicksToUTCDateTime(long ticks)
    {
        return new DateTime(ticks * 10000 + Jan1st1970Ticks);
    }

    public LongDateTime(long ticks)
    {
        ticks = MakeValidDate(ticks);
        Value = ticks;
    }

    public LongDateTime(double doubleTicks)
    {
        doubleTicks = MakeValidDate((long)(doubleTicks * MillisecondsPerDay));
        Value = (long)doubleTicks;
    }

    public LongDateTime(DateTime dateTime)
    {
        Value = DateTimeToTicks(dateTime);
    }

    public LongDateTime(int year, int month, int day)
    {
        Value = DateTimeToTicks(new DateTime(year, month, day));
    }

    public LongDateTime(int year, int month, int day, int hour, int minute, int second)
    {
        Value = DateTimeToTicks(new DateTime(year, month, day, hour, minute, second));
    }

    public LongDateTime(int year, int month, int day, int hour, int minute, int second, int millisecond)
    {
        Value = DateTimeToTicks(new DateTime(year, month, day, hour, minute, second, millisecond));
    }

    public LongDateTime(LongDateTime dtValue)
    {
        Value = dtValue.Value;
    }

    public bool IsValidDate => Value is >= 0 and <= Dec31th9999Ticks - Jan1st1970Ticks;

    public DateTime DateTime
    {
        get => TicksToDateTime(Value);
        set => Value = DateTimeToTicks(value);
    }

    public static bool CheckValidDate(long ticks) => ticks is >= 0 and <= Dec31th9999Ticks - Jan1st1970Ticks;

    public static long MakeValidDate(long ticks)
    {
        if (ticks < 0)
            ticks = 0;
        if (ticks > Dec31th9999Ticks - Jan1st1970Ticks)
            ticks = Dec31th9999Ticks - Jan1st1970Ticks;

        return ticks;
    }

    public void AddMilliseconds(double milliseconds)
    {
        Value = DateTimeToTicks(DateTime.AddMilliseconds(milliseconds));
    }

    public void AddSeconds(double seconds)
    {
        Value = DateTimeToTicks(DateTime.AddSeconds(seconds));
    }

    public void AddMinutes(double minutes)
    {
        Value = DateTimeToTicks(DateTime.AddMinutes(minutes));
    }

    public void AddHours(double hours)
    {
        Value = DateTimeToTicks(DateTime.AddHours(hours));
    }

    public void AddDays(double days)
    {
        Value = DateTimeToTicks(DateTime.AddDays(days));
    }

    public void AddMonths(int months)
    {
        Value = DateTimeToTicks(DateTime.AddMonths(months));
    }

    public void AddYears(int years)
    {
        Value = DateTimeToTicks(DateTime.AddYears(years));
    }

    public static long operator -(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value - dtValue2.Value;
    }

    public static long operator +(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value + dtValue2.Value;
    }

    public static LongDateTime operator -(LongDateTime dtValue1, long dtValue2)
    {
        dtValue1.Value -= dtValue2;
        return dtValue1;
    }

    public static LongDateTime operator +(LongDateTime dtValue1, long dtValue2)
    {
        dtValue1.Value += dtValue2;
        return dtValue1;
    }

    public static LongDateTime operator ++(LongDateTime dtValue)
    {
        dtValue.AddDays(1);
        return dtValue;
    }

    public static LongDateTime operator --(LongDateTime dtValue)
    {
        dtValue.AddDays(-1);
        return dtValue;
    }

    public static implicit operator long(LongDateTime dtValue)
    {
        return dtValue.Value;
    }

    public static implicit operator LongDateTime(long ticks)
    {
        return new LongDateTime(ticks);
    }

    public static implicit operator LongDateTime(DateTime dt)
    {
        return new LongDateTime(dt);
    }

    public static implicit operator DateTime(LongDateTime dt)
    {
        return dt.DateTime;
    }

    public static implicit operator double(LongDateTime dtValue)
    {
        return dtValue.DoubleValue;
    }

    public static implicit operator LongDateTime(double ticks)
    {
        return new LongDateTime(ticks);
    }

    public static bool operator ==(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value == dtValue2.Value;
    }

    public static bool operator !=(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value != dtValue2.Value;
    }

    public static bool operator ==(LongDateTime dtValue, long ticks)
    {
        return dtValue.Value == ticks;
    }

    public static bool operator !=(LongDateTime dtValue, long ticks)
    {
        return dtValue.Value != ticks;
    }

    public static bool operator <(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value < dtValue2.Value;
    }

    public static bool operator >(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value > dtValue2.Value;
    }

    public static bool operator <=(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value <= dtValue2.Value;
    }

    public static bool operator >=(LongDateTime dtValue1, LongDateTime dtValue2)
    {
        return dtValue1.Value >= dtValue2.Value;
    }

    public static bool operator <(LongDateTime dtValue, long ticks)
    {
        return dtValue.Value < ticks;
    }

    public static bool operator >(LongDateTime dtValue, long ticks)
    {
        return dtValue.Value > ticks;
    }

    public static bool operator <=(LongDateTime dtValue, long ticks)
    {
        return dtValue.Value <= ticks;
    }

    public static bool operator >=(LongDateTime dtValue, long ticks)
    {
        return dtValue.Value >= ticks;
    }

    public override bool Equals(object obj)
    {
        if (obj is LongDateTime dtValue)
        {
            return dtValue.Value == Value;
        }
        else if (obj is long longValue)
        {
            return longValue == Value;
        }
        else
        {
            return false;
        }
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    int IComparable.CompareTo(object obj)
    {
        if (obj is not LongDateTime time) throw new ArgumentException();
        return (Value).CompareTo(time.Value);
    }

    public string ToString(long ticks)
    {
        return ToString(ticks, DefaultFormatString);
    }

    public override string ToString()
    {
        return ToString(Value, DefaultFormatString);
    }

    public string ToString(string fmtStr)
    {
        return ToString(Value, fmtStr);
    }

    public static string ToString(long dtValue, string fmtStr)
    {
        DateTime dt = TicksToDateTime(dtValue);
        return dt.ToString(fmtStr);
    }

    public bool Equals(LongDateTime dtValue)
    {
        return Value == dtValue.Value;
    }

    public bool Equals(long ticks)
    {
        return Value == ticks;
    }

    public bool Equals(DateTime datetime)
    {
        return Value == (new LongDateTime(datetime)).Value;
    }

    public string DateTimeString => DateTime.DateTimeString();

    public string DateString => DateTime.DateString();

    public string TimeString => DateTime.TimeString();

    public string HourMinuteString => DateTime.ToString("HH:mm");

    public int Year => DateTime.Year;
    public int Month => DateTime.Month;
    public int Day => DateTime.Day;
    public int Hour => DateTime.Hour;
    public int Minute => DateTime.Minute;
    public int Second => DateTime.Second;
    public int Millisecond => DateTime.Millisecond;
}